#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fonctions.h"
#include "bit.h"
#include "cryptage.h"

#define BUFF_T 1 // 1 octects par la suite
#define TAILLE_B 8 // 8 octects par la suite

static void renommage_c(const char * nomFichier, char *nomFichier_n) {
/** Précondition : nomFichier non vide et nomFichier_n initialise pour un renommage en .clyd
  * Postcondition : nomFichier_n = nomFichier + .clyd
  */

    strcpy(nomFichier_n, nomFichier);

    nomFichier_n[strlen(nomFichier)] = '.';
    nomFichier_n[strlen(nomFichier) + 1] = 'c';
    nomFichier_n[strlen(nomFichier) + 2] = 'l';
    nomFichier_n[strlen(nomFichier) + 3] = 'y';
    nomFichier_n[strlen(nomFichier) + 4] = 'd';
    nomFichier_n[strlen(nomFichier) + 5] = '\0';
}

static void renommage_d(const char* nomFichier, char *nomFichier_n) {
/** Précondition : nomFichier non vide et nomFichier_n initialise pour un renommage
  * Postcondition : nomFichier_n = nomFichier - .clyd
  */

	int i = 0, pos=0;
	short verif_ext = 0;

	while (nomFichier[i] != '\0') {
		if(nomFichier[i] == '.'){ // on a trouvé un point
			pos = i;
		}
		else if(pos != 0){ // On verifie si après le point on bien clyd
			if(nomFichier[i] != 'c' && i-pos == 1){
				pos = 0;
			}
			else if(nomFichier[i] != 'l' && i-pos == 2){
				pos = 0;
			}
			else if(nomFichier[i] != 'y' && i-pos == 3){
				pos = 0;
			}
			else if(nomFichier[i] != 'd' && i-pos == 4){
				pos = 0;
			}
			if(nomFichier[i + 1] == '\0' && i-pos + 1 == 5){
				verif_ext = 1;
				break;
			}
		}
		verif_ext = 0;
		i++;
	}

	if(verif_ext == 1) {
		strncpy(nomFichier_n, nomFichier, pos);
		nomFichier_n[pos] = '\0';

	}
}

bool cbc_des_c(const char *nomFichier, unsigned char *clef) {

    unsigned char temp[8];
    unsigned char texte_chiffre[8] = {0};
    char nomFichier_n[strlen(nomFichier) + 5];

    unsigned char block[TAILLE_B] = {0};

    FILE* fichier_clair = fopen(nomFichier, "rb");

    if(!fichier_clair) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }

    renommage_c(nomFichier, nomFichier_n);

    FILE* fichier_chiffre = fopen(nomFichier_n, "wb");

    int i = 0; // compteur de bit

    long taille; // on va optenir la taille du fichier pour l'enregistrer.
    fseek(fichier_clair, 0, SEEK_END);
    taille = ftell(fichier_clair);
    rewind(fichier_clair);
    fwrite(&taille, sizeof(long), BUFF_T, fichier_chiffre);

    while(!feof(fichier_clair) && i < 8) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        i++;
    }
    DES_crypter(block, texte_chiffre, clef); //Chiffrement du vecteur d'initialisation
    fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

    i = 0;

    while(1) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        if(feof(fichier_clair)) {   // si on a detecté une fin de fichier, il est possible que le fichier ne soit pas un mutliple de 64octets.
                                    // on le gère alors ici.

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

            break;
        }
        if(i == 7) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

        }
        if(i < 7) i++;
        else i = 0;

    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove(nomFichier);

    return true;
}

bool cbc_des_d(const char *nomFichier, unsigned char *clef) {

    unsigned char temp[8];
    unsigned char texte_clair[8] = {0};
    char nomFichier_n[strlen(nomFichier) - 5];

    unsigned char block[TAILLE_B] = {0};
    unsigned char blockAncien[TAILLE_B] = {0};
    FILE* fichier_chiffre = fopen(nomFichier, "rb");

    if(!fichier_chiffre) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }
    renommage_d(nomFichier, nomFichier_n);

    FILE* fichier_clair = fopen(nomFichier_n, "wb");

    int i = 0;

    long taille_c, taille_d, taille_diff; // on va lire la taille pour pouvoir redonner le fichier décrypter avec la taille d'origine.
    fseek(fichier_chiffre, 0, SEEK_END);
    taille_d = ftell(fichier_chiffre);
    rewind(fichier_chiffre);
    fread(&taille_c, sizeof(long), 1, fichier_chiffre);
    taille_diff = taille_d - taille_c - sizeof(long);

    for(i = 0; i < taille_c - 8; i = i + 8) {
        if(i == 0) {

            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, texte_clair, clef); // Dechiffrement du vecteur d'initialisation
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
        else {
            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, temp, NULL);
            xor_bit(blockAncien, temp, texte_clair, 64);
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
    }
    for(i = 0; i < 8; i++) {

        fread(&(block[i]), sizeof(char), 1, fichier_chiffre);
    }


    DES_decrypter(block, temp, NULL);
    xor_bit(blockAncien, temp, texte_clair, 64);
    fwrite(texte_clair, sizeof(char), TAILLE_B - taille_diff, fichier_clair);

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove(nomFichier);

    return true;
}

bool cesar_c(const char *nomFichier, unsigned char *clef) {

    char nomFichier_n[strlen(nomFichier) + 5];

    unsigned char caractere_c, caractere_d;
    FILE* fichier_clair = fopen(nomFichier, "rb");

    int verif;

    if(!fichier_clair) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }
    renommage_c(nomFichier, nomFichier_n);

    FILE* fichier_chiffre = fopen(nomFichier_n, "wb");

    while(!feof(fichier_clair)) { // on lit caractère par caractère

        verif = fread(&caractere_c, sizeof(char), 1, fichier_clair);
        if(verif == 1) {

            CESAR_crypter(caractere_c, &caractere_d, clef);
            fwrite(&caractere_d, sizeof(char), 1, fichier_chiffre);
        }
    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove(nomFichier);

    return true;
}

bool cesar_d(const char *nomFichier, unsigned char *clef) {

    char nomFichier_n[strlen(nomFichier) - 5];
    int verif;

    unsigned char caractere_c, caractere_d;
    FILE* fichier_chiffre = fopen(nomFichier, "rb");

    if(!fichier_chiffre) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }
    renommage_d(nomFichier, nomFichier_n);

    FILE* fichier_clair = fopen(nomFichier_n, "wb");

    while(!feof(fichier_chiffre)) {

        verif = fread(&caractere_d, sizeof(char), 1, fichier_chiffre);
        if(verif == 1) {
            CESAR_decrypter(caractere_d, &caractere_c, clef);

            fwrite(&caractere_c, sizeof(char), 1, fichier_clair);
        }
    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove(nomFichier);

    return true;
}

bool cbc_troisdes_c(const char *nomFichier, unsigned char *clef) {

    /**** L'algorithme est un peu chargé mais il s'agit simplement t'appliquer 3 fois DES ****/
    /**** C'est méthode de chiffrement est assez solide ****/
    /**** Une autre possibilité aurait été chiffrement, dechiffrement et chiffrement avec des clefs differentes ****/

    unsigned char temp[8];
    unsigned char texte_chiffre[8] = {0};
    char nomFichier_n[strlen(nomFichier) + 5];

    unsigned char clef_deux[8] = {0}, clef_trois[8] = {0};
    DES_crypter(clef, clef_deux, clef);

    DES_crypter(clef_deux, clef_trois, clef); // On a généré 2 autres clefs à partir de la premiere.

    unsigned char block[TAILLE_B] = {0};

    FILE* fichier_clair = fopen(nomFichier, "rb");

    if(!fichier_clair) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }
    renommage_c(nomFichier, nomFichier_n);

    FILE* fichier_chiffre = fopen("./TMP/~tmp_1", "wb");

    int i = 0;

    /**************** Première application de DES ****************/

    long taille;
    fseek(fichier_clair, 0, SEEK_END);
    taille = ftell(fichier_clair);
    rewind(fichier_clair);
    fwrite(&taille, sizeof(long), BUFF_T, fichier_chiffre);

    while(!feof(fichier_clair) && i < 8) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        i++;
    }
    DES_crypter(block, texte_chiffre, clef); //Chiffrement du vecteur d'initialisation
    fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

    i = 0;

    while(1) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        if(feof(fichier_clair)) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

            break;
        }
        if(i == 7) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

        }
        if(i < 7) i++;
        else i = 0;

    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    /**************** Deuxieme application de DES ****************/

    i = 0;

    fichier_clair = fopen("./TMP/~tmp_1", "rb");

    if(!fichier_clair) {
        fprintf(stderr, "Probleme de memoire tempon lors du cryptage de %s\nCode de l'erreur : 002\n", nomFichier);
        return false;
    }

    fichier_chiffre = fopen("./TMP/~tmp_2", "wb");

    fseek(fichier_clair, 0, SEEK_END);
    taille = ftell(fichier_clair);
    rewind(fichier_clair);
    fwrite(&taille, sizeof(long), BUFF_T, fichier_chiffre);

    while(!feof(fichier_clair) && i < 8) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        i++;
    }
    DES_crypter(block, texte_chiffre, clef_deux); //Chiffrement du vecteur d'initialisation
    fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

    i = 0;

    while(1) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        if(feof(fichier_clair)) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

            break;
        }
        if(i == 7) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

        }
        if(i < 7) i++;
        else i = 0;

    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove("./TMP/~tmp_1");

    /**************** Toisième application de DES ****************/

    i = 0;

    fichier_clair = fopen("./TMP/~tmp_2", "rb");

    if(!fichier_clair) {
        fprintf(stderr, "Probleme de memoire tempon lors du cryptage de %s\nCode de l'erreur : 002\n", nomFichier);
        return false;
    }

    fichier_chiffre = fopen(nomFichier_n, "wb");

    fseek(fichier_clair, 0, SEEK_END);
    taille = ftell(fichier_clair);
    rewind(fichier_clair);
    fwrite(&taille, sizeof(long), BUFF_T, fichier_chiffre);

    while(!feof(fichier_clair) && i < 8) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        i++;
    }
    DES_crypter(block, texte_chiffre, clef_trois); //Chiffrement du vecteur d'initialisation
    fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

    i = 0;

    while(1) {

        fread(&(block[i]), sizeof(char), 1, fichier_clair);
        if(feof(fichier_clair)) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

            break;
        }
        if(i == 7) {

            xor_bit(block, texte_chiffre, temp, 64);
            DES_crypter(temp, texte_chiffre, NULL);
            fwrite(texte_chiffre, sizeof(char), TAILLE_B, fichier_chiffre);

        }
        if(i < 7) i++;
        else i = 0;

    }

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove("./TMP/~tmp_2");

    remove(nomFichier);

    return true;
}

bool cbc_troisdes_d(const char *nomFichier, unsigned char *clef) {


    unsigned char temp[8];
    unsigned char texte_clair[8] = {0};
    char nomFichier_n[strlen(nomFichier) - 5];

    unsigned char block[TAILLE_B] = {0};
    unsigned char blockAncien[TAILLE_B] = {0};

    unsigned char clef_deux[8] = {0}, clef_trois[8] = {0};
    DES_crypter(clef, clef_deux, clef);

    DES_crypter(clef_deux, clef_trois, clef); // On a généré 2 autres clefs à partir de la premiere.

    /******** Première application de DES **********/

    FILE* fichier_chiffre = fopen(nomFichier, "rb");

    if(!fichier_chiffre) {
        fprintf(stderr, "Le fichier %s n'a pas pu etre ouvert\n", nomFichier);
        return false;
    }
    renommage_d(nomFichier, nomFichier_n);

    FILE* fichier_clair = fopen("./TMP/~tmp_1", "wb");

    int i = 0;

    long taille_c, taille_d, taille_diff;
    fseek(fichier_chiffre, 0, SEEK_END);
    taille_d = ftell(fichier_chiffre);
    rewind(fichier_chiffre);
    fread(&taille_c, sizeof(long), 1, fichier_chiffre);
    taille_diff = taille_d - taille_c - sizeof(long);

    for(i = 0; i < taille_c - 8; i = i + 8) {
        if(i == 0) {

            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, texte_clair, clef_trois); // Dechiffrement du vecteur d'initialisation
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
        else {
            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, temp, NULL);
            xor_bit(blockAncien, temp, texte_clair, 64);
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
    }
    for(i = 0; i < 8; i++) {

        fread(&(block[i]), sizeof(char), 1, fichier_chiffre);
    }


    DES_decrypter(block, temp, NULL);
    xor_bit(blockAncien, temp, texte_clair, 64);
    fwrite(texte_clair, sizeof(char), TAILLE_B - taille_diff, fichier_clair);

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    /******** Deuxième application de DES **********/

    fichier_chiffre = fopen("./TMP/~tmp_1", "rb");

    if(!fichier_chiffre) {
        fprintf(stderr, "Probleme de memoire tempon lors du cryptage de %s\nCode de l'erreur : 012\n", nomFichier);
        return false;
    }

    fichier_clair = fopen("./TMP/~tmp_2", "wb");

    i = 0;
    fseek(fichier_chiffre, 0, SEEK_END);
    taille_d = ftell(fichier_chiffre);
    rewind(fichier_chiffre);
    fread(&taille_c, sizeof(long), 1, fichier_chiffre);
    taille_diff = taille_d - taille_c - sizeof(long);

    for(i = 0; i < taille_c - 8; i = i + 8) {
        if(i == 0) {

            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, texte_clair, clef_deux); // Dechiffrement du vecteur d'initialisation
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
        else {
            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, temp, NULL);
            xor_bit(blockAncien, temp, texte_clair, 64);
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
    }
    for(i = 0; i < 8; i++) {

        fread(&(block[i]), sizeof(char), 1, fichier_chiffre);
    }


    DES_decrypter(block, temp, NULL);
    xor_bit(blockAncien, temp, texte_clair, 64);
    fwrite(texte_clair, sizeof(char), TAILLE_B - taille_diff, fichier_clair);

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove("./TMP/~tmp_1");

    /******** Troisème application de DES **********/

    fichier_chiffre = fopen("./TMP/~tmp_2", "rb");

    if(!fichier_chiffre) {
        fprintf(stderr, "Probleme de memoire tempon lors du cryptage de %s\nCode de l'erreur : 013\n", nomFichier);
        return false;
    }

    fichier_clair = fopen(nomFichier_n, "wb");

    i = 0;

    fseek(fichier_chiffre, 0, SEEK_END);
    taille_d = ftell(fichier_chiffre);
    rewind(fichier_chiffre);
    fread(&taille_c, sizeof(long), 1, fichier_chiffre);
    taille_diff = taille_d - taille_c - sizeof(long);

    for(i = 0; i < taille_c - 8; i = i + 8) {
        if(i == 0) {

            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, texte_clair, clef); // Dechiffrement du vecteur d'initialisation
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
        else {
            fread(block, sizeof(char), 8, fichier_chiffre);

            DES_decrypter(block, temp, NULL);
            xor_bit(blockAncien, temp, texte_clair, 64);
            fwrite(texte_clair, sizeof(char), TAILLE_B, fichier_clair);
            memcpy(blockAncien, block, 8);
        }
    }
    for(i = 0; i < 8; i++) {

        fread(&(block[i]), sizeof(char), 1, fichier_chiffre);
    }


    DES_decrypter(block, temp, NULL);
    xor_bit(blockAncien, temp, texte_clair, 64);
    fwrite(texte_clair, sizeof(char), TAILLE_B - taille_diff, fichier_clair);

    fclose(fichier_clair);
    fclose(fichier_chiffre);

    remove("./TMP/~tmp_2");

    remove(nomFichier);

    return true;

}
